/*-------------------------------------------------------------------------
 *
 * tupconvert.c
 *	  元组转换支持。
 *
 * 这些函数提供在逻辑上等效但可能具有不同顺序列或不同删除列集合的行类型之间的转换。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/common/tupconvert.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/tupconvert.h"
#include "executor/tuptable.h"


/*
 * 转换设置例程具有以下公共 API：
 *
 * 设置例程使用 attmap.c 检查给定的源和目标元组描述符是否在逻辑上兼容。如果不兼容，将抛出错误。如果兼容，则在物理上兼容（即不需要转换）时返回 NULL，否则返回一个 TupleConversionMap，可用于 execute_attr_map_tuple 或 execute_attr_map_slot 进行转换。
 *
 * 如果需要，TupleConversionMap 在调用者的内存上下文中分配。还要注意，给定的元组描述符被映射引用，因此它们必须在需要映射的整个时间内存活。
 *
 * 调用者必须提供一个合适的主要错误消息，以便在抛出兼容性错误时使用。建议的编码实践是对该字符串使用 gettext_noop()，以便它可翻译，但在错误未抛出时不会实际被翻译。
 *
 *
 * 实现说明：
 *
 * TupleConversionMap 的关键组成部分是一个 attrMap[] 数组，每个输出列有一个条目。该条目包含相应输入列的 1 基索引，或零以强制 NULL 值（针对被丢弃的输出列）。TupleConversionMap 还包含工作空间数组。
 */


/*
 * 为元组转换做好准备，通过位置匹配输入和输出列。（被丢弃的列在输入和输出中都被忽略。）
 */
TupleConversionMap * convert_tuples_by_position(TupleDesc fc_indesc,
						   TupleDesc fc_outdesc,
						   const char *fc_msg)
{
	TupleConversionMap *fc_map;
	int			fc_n;
	AttrMap    *fc_attrMap;

	/* 验证兼容性并准备属性编号映射 */
	fc_attrMap = build_attrmap_by_position(fc_indesc, fc_outdesc, fc_msg);

	if (fc_attrMap == NULL)
	{
		/* 不需要运行时转换 */
		return NULL;
	}

	/* 准备映射结构 */
	fc_map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
	fc_map->indesc = fc_indesc;
	fc_map->outdesc = fc_outdesc;
	fc_map->attrMap = fc_attrMap;
	/* 为 Datum 数组预分配工作空间 */
	fc_n = fc_outdesc->natts + 1;		/* +1 以表示 NULL */
	fc_map->outvalues = (Datum *) palloc(fc_n * sizeof(Datum));
	fc_map->outisnull = (bool *) palloc(fc_n * sizeof(bool));
	fc_n = fc_indesc->natts + 1;		/* +1 以表示 NULL */
	fc_map->invalues = (Datum *) palloc(fc_n * sizeof(Datum));
	fc_map->inisnull = (bool *) palloc(fc_n * sizeof(bool));
	fc_map->invalues[0] = (Datum) 0;	/* 设置 NULL 条目 */
	fc_map->inisnull[0] = true;

	return fc_map;
}

/*
 * 为元组转换做好准备，通过名称匹配输入和输出列。
 * （被丢弃的列在输入和输出中都被忽略。）这用意在于当行类型通过继承相关时使用，因此我们期待类型和 typmod 的完全匹配。错误消息会有点不太有用，除非两个行类型都是命名的复合类型。
 */
TupleConversionMap * convert_tuples_by_name(TupleDesc fc_indesc,
					   TupleDesc fc_outdesc)
{
	TupleConversionMap *fc_map;
	AttrMap    *fc_attrMap;
	int			fc_n = fc_outdesc->natts;

	/* 验证兼容性并准备属性编号映射 */
	fc_attrMap = build_attrmap_by_name_if_req(fc_indesc, fc_outdesc);

	if (fc_attrMap == NULL)
	{
		/* 不需要运行时转换 */
		return NULL;
	}

	/* 准备映射结构 */
	fc_map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap));
	fc_map->indesc = fc_indesc;
	fc_map->outdesc = fc_outdesc;
	fc_map->attrMap = fc_attrMap;
	/* 为 Datum 数组预分配工作空间 */
	fc_map->outvalues = (Datum *) palloc(fc_n * sizeof(Datum));
	fc_map->outisnull = (bool *) palloc(fc_n * sizeof(bool));
	fc_n = fc_indesc->natts + 1;		/* +1 以表示 NULL */
	fc_map->invalues = (Datum *) palloc(fc_n * sizeof(Datum));
	fc_map->inisnull = (bool *) palloc(fc_n * sizeof(bool));
	fc_map->invalues[0] = (Datum) 0;	/* 设置 NULL 条目 */
	fc_map->inisnull[0] = true;

	return fc_map;
}

/*
 * 根据映射执行元组的转换。
 */
HeapTuple execute_attr_map_tuple(HeapTuple fc_tuple, TupleConversionMap *fc_map)
{
	AttrMap    *fc_attrMap = fc_map->attrMap;
	Datum	   *fc_invalues = fc_map->invalues;
	bool	   *fc_inisnull = fc_map->inisnull;
	Datum	   *fc_outvalues = fc_map->outvalues;
	bool	   *fc_outisnull = fc_map->outisnull;
	int			fc_i;

	/*
	 * 提取旧元组的所有值，偏移数组，使得 invalues[0] 保持 NULL，invalues[1] 是第一个源属性；
	 * 这完全符合 attrMap 中的编号约定。
	 */
	heap_deform_tuple(fc_tuple, fc_map->indesc, fc_invalues + 1, fc_inisnull + 1);

	/*
	 * 转置到新元组的正确字段中。
	 */
	Assert(fc_attrMap->maplen == fc_map->outdesc->natts);
	for (fc_i = 0; fc_i < fc_attrMap->maplen; fc_i++)
	{
		int			fc_j = fc_attrMap->attnums[fc_i];

		fc_outvalues[fc_i] = fc_invalues[fc_j];
		fc_outisnull[fc_i] = fc_inisnull[fc_j];
	}

	/*
	 * 现在形成新元组。
	 */
	return heap_form_tuple(fc_map->outdesc, fc_outvalues, fc_outisnull);
}

/*
 * 根据映射执行元组槽的转换。
 */
TupleTableSlot * execute_attr_map_slot(AttrMap *fc_attrMap,
					  TupleTableSlot *fc_in_slot,
					  TupleTableSlot *fc_out_slot)
{
	Datum	   *fc_invalues;
	bool	   *fc_inisnull;
	Datum	   *fc_outvalues;
	bool	   *fc_outisnull;
	int			fc_outnatts;
	int			fc_i;

	/* 合理性检查 */
	Assert(fc_in_slot->tts_tupleDescriptor != NULL &&
		   fc_out_slot->tts_tupleDescriptor != NULL);
	Assert(fc_in_slot->tts_values != NULL && fc_out_slot->tts_values != NULL);

	fc_outnatts = fc_out_slot->tts_tupleDescriptor->natts;

	/* 提取输入槽的所有值。 */
	slot_getallattrs(fc_in_slot);

	/* 在做映射之前，清除输出槽中的任何旧内容 */
	ExecClearTuple(fc_out_slot);

	fc_invalues = fc_in_slot->tts_values;
	fc_inisnull = fc_in_slot->tts_isnull;
	fc_outvalues = fc_out_slot->tts_values;
	fc_outisnull = fc_out_slot->tts_isnull;

	/* 转换成输出槽的适当字段。 */
	for (fc_i = 0; fc_i < fc_outnatts; fc_i++)
	{
		int			fc_j = fc_attrMap->attnums[fc_i] - 1;

		/* attrMap->attnums[i] == 0 代表这是一个 NULL 数据。 */
		if (fc_j == -1)
		{
			fc_outvalues[fc_i] = (Datum) 0;
			fc_outisnull[fc_i] = true;
		}
		else
		{
			fc_outvalues[fc_i] = fc_invalues[fc_j];
			fc_outisnull[fc_i] = fc_inisnull[fc_j];
		}
	}

	ExecStoreVirtualTuple(fc_out_slot);

	return fc_out_slot;
}

/*
 * 根据映射执行列位图的转换。
 *
 * 输入和输出位图的偏移量是
 * FirstLowInvalidHeapAttributeNumber，以适应系统列，比如
 * RangeTblEntry 中的列位图。
 */
Bitmapset * execute_attr_map_cols(AttrMap *fc_attrMap, Bitmapset *fc_in_cols)
{
	Bitmapset  *fc_out_cols;
	int			fc_out_attnum;

	/* 常见简单情况的快速路径 */
	if (fc_in_cols == NULL)
		return NULL;

	/*
	 * 对于每个输出列，检查它对应于哪个输入列。
	 */
	fc_out_cols = NULL;

	for (fc_out_attnum = FirstLowInvalidHeapAttributeNumber;
		 fc_out_attnum <= fc_attrMap->maplen;
		 fc_out_attnum++)
	{
		int			fc_in_attnum;

		if (fc_out_attnum < 0)
		{
			/* 系统列。没有映射。 */
			fc_in_attnum = fc_out_attnum;
		}
		else if (fc_out_attnum == 0)
			continue;
		else
		{
			/* 正常用户列 */
			fc_in_attnum = fc_attrMap->attnums[fc_out_attnum - 1];

			if (fc_in_attnum == 0)
				continue;
		}

		if (bms_is_member(fc_in_attnum - FirstLowInvalidHeapAttributeNumber, fc_in_cols))
			fc_out_cols = bms_add_member(fc_out_cols, fc_out_attnum - FirstLowInvalidHeapAttributeNumber);
	}

	return fc_out_cols;
}

/*
 * 释放 TupleConversionMap 结构体。
 */
void free_conversion_map(TupleConversionMap *fc_map)
{
	/* indesc 和 outdesc 不是我们的，所以不需要释放 */
	free_attrmap(fc_map->attrMap);
	pfree(fc_map->invalues);
	pfree(fc_map->inisnull);
	pfree(fc_map->outvalues);
	pfree(fc_map->outisnull);
	pfree(fc_map);
}
